package com.framework.core.interceptor;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.hibernate.EmptyInterceptor;
import org.hibernate.EntityMode;
import org.hibernate.Interceptor;
import org.hibernate.Session;
import org.hibernate.dialect.Dialect;
import org.hibernate.impl.SessionFactoryImpl;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.type.LiteralType;
import org.hibernate.type.TimestampType;
import org.hibernate.type.Type;

import com.framework.core.SysConst;
import com.framework.core.SysContexts;
import com.framework.core.base.TableSplitRule;
import com.framework.core.cache.CacheFactory;
import com.framework.core.cache.impl.SysTableHisConfCache;
import com.framework.core.identity.vo.BaseUser;
import com.framework.core.tablehis.vo.SysTableHisConf;
import com.framework.core.tablehis.vo.SysTableHisRecord;
import com.framework.core.util.BeanUtils;
import com.framework.core.util.DateUtil;
import com.framework.core.util.ThreadLocalUtil;
import com.framework.core.util.impl.SysConfig;

/**
 * 数据库查询拦截器，注入分表规则
 * @author wengxk
 *
 */
public class TableSplitInterceptor extends EmptyInterceptor {
	private static final long serialVersionUID = 4891533317047240043L;
	
	public static final Interceptor INSTANCE = new TableSplitInterceptor();
	private ConcurrentHashMap<Object,Object> entityMap  = new ConcurrentHashMap<Object,Object>();
	private TableSplitInterceptor() {
	}
	
	@Override
	public String onPrepareStatement(String sql) {
		Pattern p = Pattern.compile("\\w(\\{.*?\\})");
		Matcher m = p.matcher(sql);
		String value = null;
		while (m.find()) {
			String group = m.group(1);
			String key = group.substring(1, group.length()-1);
			Object obj = (Object) ThreadLocalUtil.get(key);
			if (obj == null) {
				throw new RuntimeException("未设置分表参数值:" +key);
			} else {
				if (obj instanceof String[]) {
					value = ((String[]) obj)[0];
				} else if (obj instanceof String) {
					value = (String) obj;
				} else {
					throw new RuntimeException("分表参数值非字符串，请调整:" +key);
				}
				sql = sql.replace(group, value);
			}
		}
		return super.onPrepareStatement(sql);
	}
	
	@Override
	public boolean onSave(Object entity, Serializable id, Object[] state,
			String[] propertyNames, Type[] types) {
		this.doTableHis(entity, id, state, null, propertyNames, types, SysConst.OP_TYPE.INSERT);
		return super.onSave(entity, id, state, propertyNames, types);
	}
	
	@Override
	public boolean onFlushDirty(Object entity, Serializable id,
			Object[] currentState, Object[] previousState, String[] propertyNames, Type[] atype) {
		this.doTableHis(entity, id, currentState, previousState, propertyNames, atype, SysConst.OP_TYPE.UPDATE);
		return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, atype);
	}
	
	@Override
	public void onDelete(Object entity, Serializable id, Object[] state,
			String[] propertyNames, Type[] types) {
		this.doTableHis(entity, id, null, state, propertyNames, types, SysConst.OP_TYPE.DELETE);
		super.onDelete(entity, id, state, propertyNames, types);
	}
	
	@Override
	public void postFlush(Iterator entities) {
		//提交之后如果是新增数据需要复制主键
		List<Object> tmpList = new ArrayList<Object>();
		while(entities.hasNext()){
			Object ob = entities.next();
			Object dest = entityMap.get(ob);
			if(dest!=null){
				try {
					BeanUtils.copyProperties(dest, ob);
				} catch (Exception e) {
				}
				entityMap.remove(ob);
				tmpList.add(dest);
				
			}
		}
		if(tmpList.size()>0){
			Session session = SysContexts.getEntityManager();
			for(Object dest:tmpList){
				session.save(dest);
			}
			tmpList=new ArrayList<Object>();
		}
		super.postFlush(entities);
	}
	
	public void doTableHis(Object entity, Serializable id,
			Object[] currentState, Object[] previousState, String[] propertyNames, Type[] atype,int opType){

		AbstractEntityPersister ClassMetadata =  (AbstractEntityPersister)SysContexts.getClassMetadata(entity.getClass());
		String tableName = ClassMetadata.getTableName().toLowerCase();
		
		//缓存取得有问题会造成重复进入onFlushDirty
		SysTableHisConf sysTableHisConf = (SysTableHisConf)CacheFactory.get(SysTableHisConfCache.class, tableName);
		if(sysTableHisConf==null){
			return;
		}
		Date now = new Date();
		Map<String, String> map = new HashMap<String, String>();
		map.put(TableSplitRule.Rule.yyyyMM, TableSplitRule.yyyyMM());
		Session session = SysContexts.getEntityManager(map);
		String nowStr = DateUtil.formatDate(now,DateUtil.DATETIME12_FORMAT2);
		Random rd = new Random();
		long batchId = Long.parseLong(nowStr+rd.nextInt(1000));
		long hisId = 0;
		BaseUser baseUser = SysContexts.getCurrentOperator();
		long opId = -1L;
		if(baseUser!=null){
			opId = baseUser.getOperId();
		}
		//历史表处理
		if(SysConst.SYS_TABLE_HIS_CONF_TYPE.HIS==sysTableHisConf.getType()
				||SysConst.SYS_TABLE_HIS_CONF_TYPE.BOTH==sysTableHisConf.getType()){
			String name = entity.getClass().getName();
			int point = name.lastIndexOf(".");
			String entityName = name+"His";
			Object ob =null;
			try {
				Class clazz =Class.forName(entityName);
				ob = clazz.newInstance();
				BeanUtils.copyProperties(ob, entity);
				
				try {
					Method opIdMethod = clazz.getMethod("setHisOpId", long.class);
					Method opDateMethod = clazz.getMethod("setHisCreateDate", Date.class);
					Method opTypeMethod = clazz.getMethod("setHisOpType", int.class);
					opIdMethod.invoke(ob, opId);
					opDateMethod.invoke(ob, now);
					opTypeMethod.invoke(ob, opType);
				}catch (Exception e) {
					//没有通用字段情况不处理
					throw new RuntimeException(e);
				}
				
				if(SysConst.OP_TYPE.INSERT==opType){
					//新增的情况这个时候不能保存，源表的主键拿不到，先放到一个Map
					entityMap.put(entity,ob);
				} else {
					hisId = (long) session.save(ob);
				}
			} catch (Exception e) {
				
			}
		}
		//差异表处理 只有更新的时候记录差异表
		if((SysConst.SYS_TABLE_HIS_CONF_TYPE.DIFF==sysTableHisConf.getType()
				||SysConst.SYS_TABLE_HIS_CONF_TYPE.BOTH==sysTableHisConf.getType()) && opType==SysConst.OP_TYPE.UPDATE){
			SessionFactoryImpl sessionFactory = (SessionFactoryImpl)SysConfig.getInstance().getHibernateFactory(SysConfig.DEFAULT_DB);
			Dialect dialect = sessionFactory.getDialect();
			for (int i=0; i<currentState.length; i++) {
				//判断是否相等
				boolean diff = !atype[i].isEqual(previousState[i], currentState[i], EntityMode.POJO);
				if (diff){
					//不等于保存
					SysTableHisRecord sysTableHisRecord = new SysTableHisRecord();
					sysTableHisRecord.setHisId(hisId);
					sysTableHisRecord.setTableName(tableName);
					sysTableHisRecord.setTableIdName(StringUtils.join(ClassMetadata.getIdentifierColumnNames(), ","));
					sysTableHisRecord.setTableIdValue(ClassMetadata.getIdentifier(entity, EntityMode.POJO)+"");
					sysTableHisRecord.setCloumnName(StringUtils.join(ClassMetadata.getPropertyColumnNames(propertyNames[i]),","));
					if(atype[i] instanceof LiteralType){
						LiteralType type = (LiteralType)atype[i];
						if(type instanceof TimestampType){
							if(previousState[i] instanceof Date){
								//原值在数据库 本身没有毫秒 不处理
								if(previousState[i] != null){
									sysTableHisRecord.setOldValue(DateUtil.formatDate((Date)previousState[i],DateUtil.DATETIME12_FORMAT));
								}
								if(currentState[i] != null){
									//毫秒的处理
									Date value =(Date)currentState[i];
									Timestamp ts = Timestamp.class.isInstance(value)
											? (Timestamp)value
											: new Timestamp(value.getTime());
									int nanos=ts.getNanos();
									if(nanos/500000000==1){
										Calendar calendar = Calendar.getInstance();    
									    calendar.setTime(value);    
									    calendar.add(Calendar.SECOND, 1);    
									    value = calendar.getTime();    
									}
									sysTableHisRecord.setNewValue(DateUtil.formatDate(value,DateUtil.DATETIME12_FORMAT));
								}
							} else {
								//如果不是date型需要自行提供toString方法
								if(previousState[i] != null){
									sysTableHisRecord.setOldValue(previousState[i].toString());
								}
								if(currentState[i] != null){
									sysTableHisRecord.setNewValue(currentState[i].toString());
								}
							}
							
						} else{
							try {
								if(previousState[i] != null){
									sysTableHisRecord.setOldValue(type.objectToSQLString(previousState[i], dialect).replaceAll("'", ""));
								}
								if(currentState[i] != null){
									sysTableHisRecord.setNewValue(type.objectToSQLString(currentState[i], dialect).replaceAll("'", ""));
								}
							} catch (Exception e) {
								if(previousState[i] != null){
									sysTableHisRecord.setOldValue(previousState[i].toString());
								}
								if(currentState[i] != null){
									sysTableHisRecord.setNewValue(currentState[i].toString());
								}
							}
						}
					}else{
						if(previousState[i] != null){
							sysTableHisRecord.setOldValue(previousState[i].toString());
						}
						if(currentState[i] != null){
							sysTableHisRecord.setNewValue(currentState[i].toString());
						}
					}
					sysTableHisRecord.setBatchId(batchId);
					sysTableHisRecord.setOpId(opId);
					sysTableHisRecord.setCreateDate(now);
					session.save(sysTableHisRecord);
				}
			}
		}
	}
}
